#include "as5047p.h"



/*************************************************
 * @Function: Calculate the even parity bit of a 16-bit unsigned integer.
 * @Description: This function takes a 16-bit unsigned integer as input and calculates its even parity bit.
 *               Even parity means that the total number of 1s in the binary representation of the number,
 *               including the parity bit, should be an even number.
 * @Calls:  None
 * @CalledBy: Functions that need to perform parity check or add parity bits,
 *            such as as5047p_read_angle when constructing the command with parity.
 * @param:  v - A 16-bit unsigned integer for which the even parity bit needs to be calculated.
 * @Output: None
 * @Return: The even parity bit of the input 16-bit unsigned integer, which is either 0 or 1.
 * @Others: This function uses bitwise XOR operations to efficiently calculate the parity bit.
 *          It first checks if the input is 0, in which case the parity bit is 0.
*************************************************/
static uint8_t as5047p_parity_even(uint16_t v)
{
      if(v == 0) return 0;


      v ^= v >> 8;
      v ^= v >> 4;
      v ^= v >> 2;
      v ^= v >> 1;
      return v & 1;
}


/*************************************************
 * @Function: Control the chip select (CS) pin of the AS5047P sensor.
 * @Description: This function is used to set the state of the chip select (CS) pin of the AS5047P sensor.
 *               When the 'status' parameter is true, the CS pin is set to the low level (GPIO_PIN_RESET),
 *               indicating that the sensor is selected; when 'status' is false, the CS pin is set to the high level (GPIO_PIN_SET),
 *               indicating that the sensor is deselected.
 * @Calls:  HAL_GPIO_WritePin
 * @CalledBy: Functions that need to communicate with the AS5047P sensor via SPI,
 *            such as as5047p_spi_write_data_2byte.
 * @param:  obj - Pointer to the as5047p_t structure, which contains the GPIO port and pin information for the CS pin.
 *          status - A boolean value indicating whether to select the sensor (true) or deselect it (false).
 * @Output: None
 * @Return: The input 'status' value, which can be used to confirm the operation result if needed.
 * @Others: This function uses the HAL library's GPIO write function to control the CS pin state.
*************************************************/
static bool as5047p_spi_set_cs(as5047p_t* obj,bool status)
{

    HAL_GPIO_WritePin(obj->GPIO_PORT_CS, obj->GPIO_PIN_CS, status ? GPIO_PIN_RESET : GPIO_PIN_SET);

	return status;

}




/*************************************************
 * @Function: Write a 2-byte data to the AS5047P sensor via SPI and receive a 2-byte response.
 * @Description: This function is responsible for enabling the chip select (CS) pin of the AS5047P sensor,
 *               transmitting a 2-byte data packet to the sensor using SPI communication,
 *               receiving a 2-byte response from the sensor, and then disabling the CS pin.
 * @Calls: as5047p_spi_set_cs, HAL_SPI_TransmitReceive
 * @CalledBy: Functions that need to communicate with the AS5047P sensor via SPI, such as as5047p_read_angle.
 * @param: obj - Pointer to the as5047p_t structure, which contains the sensor's configuration information.
 *          tx_buff - The 2-byte data packet to be transmitted to the AS5047P sensor.
 * @Output: None
 * @Return: The 2-byte response received from the AS5047P sensor.
 * @Others: This function uses the HAL library's SPI transmit and receive function, and the timeout is set to 100ms.
*************************************************/
/*************************************************
 * @Function: 通过 SPI 向 AS5047P 传感器写入 2 字节数据并接收 2 字节响应
 * @Description: 此函数负责使能 AS5047P 传感器的片选 (CS) 引脚，
 *               使用 SPI 通信向传感器发送 2 字节数据包，
 *               从传感器接收 2 字节响应，然后禁用 CS 引脚。
 * @Calls: as5047p_spi_set_cs, HAL_SPI_TransmitReceive
 * @CalledBy: 需要通过 SPI 与 AS5047P 传感器通信的函数，例如 as5047p_read_angle。
 * @param: obj - 指向 as5047p_t 结构体的指针，包含传感器的配置信息。
 *          tx_buff - 要发送给 AS5047P 传感器的 2 字节数据包。
 * @Output: 无
 * @Return: 从 AS5047P 传感器接收到的 2 字节响应。
 * @Others: 此函数使用 HAL 库的 SPI 发送和接收函数，超时时间设置为 100 毫秒。
*************************************************/
static uint16_t as5047p_spi_write_data_2byte(as5047p_t* obj,uint16_t tx_buff)
{
	uint16_t rx_buff;//read buffer

	as5047p_spi_set_cs(obj,true);//enable cs pin

	HAL_SPI_TransmitReceive(obj->hspi, (uint8_t *)&tx_buff, (uint8_t *)&rx_buff, 1, 100);//spi communicate

	as5047p_spi_set_cs(obj,false);//disable cs pin

	return rx_buff;//return read buffer
}


/*************************************************
 * @Function: Read angle data from the AS5047P sensor.
 * @Description: This function is used to send a read command to the AS5047P sensor,
 *               and parse the angle information based on the returned data.
 *               It also checks the parity bit of the returned data.
 *               If the parity check fails, it sets the error flag.
 * @Calls:  as5047p_parity_even, as5047p_spi_write_data_2byte
 * @CalledBy: Higher-level business logic functions that call this function,
 *            depending on the overall code structure.
 * @param:  obj - Pointer to the as5047p_t structure, containing sensor-related configuration information.
 * @Output: None
 * @Return: Returns 1 if an error occurs during the reading process (parity check fails or other errors);
 *          returns 0 if the reading is successful.
 * @Others: The read angle data will be stored in the angle_data member of the as5047p_t structure,
 *          and the error flag will be stored in the error_flag member.
*************************************************/
/*************************************************
 * @Function: 从 AS5047P 传感器读取角度数据
 * @Description: 该函数用于向 AS5047P 传感器发送读取命令，并根据返回数据解析角度信息。同时会检查返回数据的奇偶校验位，若校验失败则设置错误标志。
 * @Calls:  as5047p_parity_even, as5047p_spi_write_data_2byte
 * @CalledBy: 调用该函数的上层业务逻辑函数，具体取决于代码整体结构
 * @param:  obj - 指向 as5047p_t 结构体的指针，包含传感器相关配置信息
 * @Output: 无
 * @Return: 若读取过程中出现错误（奇偶校验失败或其他错误），返回 1；若读取成功，返回 0
 * @Others: 读取的角度数据会存储在 as5047p_t 结构体的 angle_data 成员中，错误标志存储在 error_flag 成员中
*************************************************/
uint8_t as5047p_read_encoder(as5047p_t* obj)
{
	uint16_t data = 0;
	uint16_t rx_buff;
	uint16_t command = 0x4000;// PAR=0 R/W=R


	command = command | CMD_ANGLECOM;
	command |= ((uint16_t)as5047p_parity_even(command)<<15);//Add a parity bit on the the MSB

	rx_buff =as5047p_spi_write_data_2byte(obj,command);//read data from as5047p

	obj->error_flag = 1;

	if ((rx_buff & (1 << 14)) == 0){
		data = (rx_buff & 0x3FFF);
		obj->error_flag = (as5047p_parity_even(data) ^ (rx_buff >> 15));
	}

	if(obj->error_flag)
		return 1;

	obj->encoder_data = data;
	return 0;
}

/*************************************************
 * @Function: Initialize the AS5047P sensor object.
 * @Description: This function is used to initialize the members of the as5047p_t structure,
 *               including the SPI handle, GPIO port, and GPIO pin for the chip select (CS) of the AS5047P sensor.
 *               After successful initialization, the as5047p_t object can be used for subsequent sensor communication operations.
 * @Calls:  None
 * @CalledBy: Higher-level initialization functions that need to set up the AS5047P sensor object,
 *            such as system initialization routines or sensor configuration functions.
 * @param:  obj - Pointer to the as5047p_t structure to be initialized.
 *          hspi - Pointer to the SPI handle, which is used for SPI communication with the sensor.
 *          GPIO_PORT_CS - Pointer to the GPIO port where the CS pin of the sensor is located.
 *          GPIO_PIN_CS - The GPIO pin number of the CS pin of the sensor.
 * @Output: None
 * @Return: 0 indicates successful initialization. Currently, this function does not handle error conditions,
 *          but it can be extended in the future to return different values based on the initialization result.
 * @Others: This function simply assigns the input parameters to the corresponding members of the as5047p_t structure.
 *          Error handling can be added in the future if necessary.
*************************************************/
/*************************************************
 * @Function: 初始化 AS5047P 传感器对象。
 * @Description: 此函数用于初始化 as5047p_t 结构体的成员，
 *               包括 AS5047P 传感器的 SPI 句柄、片选 (CS) 引脚对应的 GPIO 端口和 GPIO 引脚号。
 *               初始化成功后，as5047p_t 对象可用于后续的传感器通信操作。
 * @Calls:  无
 * @CalledBy: 需要设置 AS5047P 传感器对象的上层初始化函数，
 *            例如系统初始化例程或传感器配置函数。
 * @param:  obj - 指向待初始化的 as5047p_t 结构体的指针。
 *          hspi - 指向 SPI 句柄的指针，用于与传感器进行 SPI 通信。
 *          GPIO_PORT_CS - 指向传感器 CS 引脚所在 GPIO 端口的指针。
 *          GPIO_PIN_CS - 传感器 CS 引脚的 GPIO 引脚号。
 * @Output: 无
 * @Return: 0 表示初始化成功。当前函数未处理错误情况，
 *          但未来可扩展以根据初始化结果返回不同的值。
 * @Others: 此函数仅将输入参数赋值给 as5047p_t 结构体的相应成员。
 *          如有需要，未来可添加错误处理逻辑。
*************************************************/
uint8_t as5047p_obj_create(as5047p_t* obj,SPI_HandleTypeDef * hspi,GPIO_TypeDef * GPIO_PORT_CS,uint16_t GPIO_PIN_CS)
{
	obj->hspi = hspi;//spi句柄

	obj->GPIO_PORT_CS=GPIO_PORT_CS;//cs引脚

	obj->GPIO_PIN_CS=GPIO_PIN_CS;//cs引脚
	
	obj->cpr = 16384;

	return 0;
}




/**
 * @function    读取AS5047P数据
 * @param[in]   cmd    寄存器地址
 * @retval      角度信息
 */
uint8_t as5047p_get_data(as5047p_t* obj)
{
	
	uint8_t ret = as5047p_read_encoder(obj);//读取角度寄存器的数据
	
	/*如果错误标志位置1，则数据为0*/
	if(ret)
	{
		return 1;//直接返回
	}
	
  	/* 处理角度数据 */
	/* 跟踪旋转次数以扩展角度范围从 [0,2PI] 到基本上无限大 */
	obj->d_encoder = obj->encoder_data - obj->encoder_data_prev; // 计算当前角度与上一次角度的差值
	
	/* 如果发生溢出，则将其视为一次完整旋转 */
	if(fabs(obj->d_encoder) > (0.8 * obj->cpr)) 
		obj->full_rotation_offset += obj->d_encoder > 0 ? -AS5047P_2PI : AS5047P_2PI; // 根据d_angle的方向调整满旋转偏移量
	
	/* 保存当前角度值以便后续步骤使用,用于判断是否发生了溢出 */
	obj->encoder_data_prev = obj->encoder_data;
	
	
	/* 返回总角度,（满旋转次数）*2PI + 当前传感器角度 */
	obj->total_angle_radian = (obj->full_rotation_offset + (obj->encoder_data / (float)obj->cpr) * AS5047P_2PI);
	
	obj->only_angle_radian = (((float)obj->encoder_data / (float)obj->cpr) * AS5047P_2PI); // 当前角度的弧度值
	
	obj->total_angle = (obj->full_rotation_offset + ((float)obj->encoder_data / (float)obj->cpr) * AS5047P_2PI) * 360 / AS5047P_2PI; // 总角度的度数值
	
	obj->only_angle = ((obj->encoder_data / (float)obj->cpr) * 360); // 当前角度的度数值
	
	return 0;
}


